package org.wisdom.data.jpa.support.service;

import java.text.DecimalFormat;
import java.util.Date;
import java.util.Locale;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.wisdom.data.jpa.annotation.SerialNumber;
import org.wisdom.data.jpa.support.entity.CodeGenerator;
import org.wisdom.utils.WisdomUtil;

import com.alibaba.druid.util.StringUtils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;

/**
 * 编码服务
 *
 * @author 张智研
 * @date 2022/05/18 15:17
 **/
@Service
public class CodeService {

    private final CodeGeneratorRepository codeGeneratorRepository;

    public CodeService(CodeGeneratorRepository codeGeneratorRepository) {
        this.codeGeneratorRepository = codeGeneratorRepository;
    }

    /**
     * 编码生成
     *
     * @author 张智研
     * @date 2022/05/19 09:11
     * @param source
     *            实体对象
     * @param serialNumber
     *            注解对象
     * @return java.lang.String
     */
    public <T> String generatorCode(T source, SerialNumber serialNumber) {
        String key = serialNumber.key();
        key = this.paramConvertValue(key, serialNumber, source);
        String value = serialNumber.value();
        value = this.paramConvertValue(value, serialNumber, source, key);
        return value;
    }

    /**
     * 参数转换为值
     *
     * @author 张智研
     * @date 2022/05/19 09:08
     * @param str
     *            字符串
     * @param serialNumber
     *            注解对象
     * @param source
     *            实体对象
     * @return java.lang.String
     */
    private <T> String paramConvertValue(String str, SerialNumber serialNumber, T source) {
        return this.paramConvertValue(str, serialNumber, source, null);
    }

    /**
     * 参数转换为值
     *
     * @author 张智研
     * @date 2022/05/19 09:08
     * @param str
     *            字符串
     * @param serialNumber
     *            注解对象
     * @param source
     *            实体对象
     * @param numberKey
     *            序号标识
     * @return java.lang.String
     */
    private <T> String paramConvertValue(String str, SerialNumber serialNumber, T source, String numberKey) {
        String[] params = WisdomUtil.getParams(str, WisdomUtil.EXP1);
        for (String param : params) {
            switch (param.toLowerCase(Locale.ROOT)) {
                case "{d}":
                    // 时间
                    String d = DateUtil.format(new Date(), serialNumber.dateFormat());
                    str = str.replace(param, d);
                    break;
                case "{t}":
                    // 表名字
                    str = str.replace(param, source.getClass().getName());
                    break;
                case "{n}":
                    // 序号
                    if (!StringUtils.isEmpty(numberKey)) {
                        long n = this.getNumber(numberKey);
                        int numberLength = serialNumber.numberLength();
                        Assert.isTrue(numberLength > 0, "序号长度必须大于0");
                        String n1 = this.numberToStr(n, numberLength);
                        str = str.replace(param, n1);
                    }
                    break;
                case "{m}":
                    // 序号
                    if (!StringUtils.isEmpty(numberKey)) {
                        long n = this.getNumber(numberKey);
                        str = str.replace(param, String.valueOf(n));
                    }
                    break;
                default:
                    String fieldKey = param.replaceAll("[{}]", "");
                    Object value = BeanUtil.getFieldValue(source, fieldKey);
                    Assert.notNull(value, "参与编号生成的实体域{" + fieldKey + "}不能为null");
                    str = str.replace(param, String.valueOf(value));
                    break;
            }
        }
        return str;
    }

    /**
     * 获取序号
     *
     * @author 张智研
     * @date 2022/05/18 17:11
     * @param genName
     *            序号标识
     * @return int
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE,
        rollbackFor = RuntimeException.class)
    public long getNumber(String genName) {
        CodeGenerator codeGenerator = this.codeGeneratorRepository.findByIdForUpdate(genName);
        long n = 0;
        if (codeGenerator != null) {
            n = codeGenerator.getGenValue();
        } else {
            codeGenerator = new CodeGenerator();
            codeGenerator.setGenName(genName);
        }
        codeGenerator.setGenValue(++n);
        this.codeGeneratorRepository.saveAndFlush(codeGenerator);
        return n;
    }

    /**
     * 数字转为固定长度字符串，前面补"0"
     *
     * @author 张智研
     * @date 2022/05/19 08:48
     * @param number
     *            数字
     * @param length
     *            长度
     * @return java.lang.String
     */
    private String numberToStr(long number, int length) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < length; i++) {
            stringBuilder.append("0");
        }
        DecimalFormat decimalFormat = new DecimalFormat(stringBuilder.toString());
        return decimalFormat.format(number);
    }
}